#include <string>
#include <iostream>

#include <cstdlib>
#include "TypeBase.hpp"
#include "SymbolTable.hpp"
#include "SymbolType.hpp"

class SymbolTable;
SymbolTable::SymbolTable() { 
	this->symbols = new MapSym();
	this->parentTable = NULL;
	this->callingContext =NULL;
	this->tempTypes = new std::map<int, TypeTemp *>;
}

SymbolTable::SymbolTable(SymbolTable * parentTable, SymbolTable * callingCont) {
	this->symbols = new MapSym();
	this->parentTable = parentTable;
	this->callingContext = callingCont;
	this->tempTypes = new std::map<int, TypeTemp *>;
}

SymbolTable::~SymbolTable() { 
	if(symbols!=NULL){
		for (MapSym::iterator it = symbols->begin(); it != symbols->end(); it++) {
			delete it->second;
		}
		delete this->symbols;
	}
	this->parentTable = NULL;
	this->callingContext = NULL;
	if(tempTypes!=NULL){
		for (std::map<int, TypeTemp*>::iterator it = tempTypes->begin(); it != tempTypes->end(); it++) 
			delete it->second;
		delete tempTypes;
	}
}

void SymbolTable::setParent(SymbolTable * parentTable) {
	this->parentTable = parentTable;
}

void SymbolTable::setName(std::string name){
	this->name = name;
}

std::string& SymbolTable::getName(){
	return this->name;
}

Symbol * SymbolTable::add(int id, Symbol * sym) {

	(*symbols)[id] = sym;
	return sym;
}

void SymbolTable::del(int id) {
	MapSym::iterator it = symbols->find(id);
	if(it != symbols->end())
		symbols->erase (it);  
}
void SymbolTable::substitute(int id, Symbol * sym) {
	if( (*symbols)[id] != NULL ) {
		delete (*symbols)[id];

	}
	this->add(id, sym);
}

Symbol * SymbolTable::getById(int id) {
	MapSym::iterator itSym = this->symbols->find( id );
	if( itSym != this->symbols->end() ) {
		return itSym->second;
	}
	if( this->callingContext != NULL ){
		return this->callingContext->getById(id);
	}

	return NULL;
}

Symbol * SymbolTable::getByIdInside(int id) {
	MapSym::iterator itSym = this->symbols->find( id );
	if( itSym != this->symbols->end() ) {
		return itSym->second;
	}
	return NULL;
}

SymbolTable * SymbolTable::getParent() {
	return this->parentTable;
}

SymbolTable * SymbolTable::getCallingCont() {
	return this->callingContext;
}

void SymbolTable::show() {
	std::string tabulation="";
	SymbolTable * p = this->getParent();
	while(p != NULL) {
		tabulation+="	";
		p = p->getParent();
	}
	std::cout << "** Table Symbole (" << this->name << ",";
	if(this->getParent()!=NULL){
		std::cout << this->getParent()->getName();
	}
	std::cout <<  ")" << std::endl;
	for (MapSym::iterator it = symbols->begin(); it != symbols->end(); it++) {
		int valueInIdentTable = it->first;
		Symbol * symbol = it->second;
		std::cout << tabulation << valueInIdentTable << "\t";
		symbol->show();
		std::cout << std::endl;
	}
}

void SymbolTable::showList() {
	for (MapSym::iterator it = symbols->begin(); it != symbols->end(); it++) {
		//int valueInIdentTable = it->first;
		Symbol * symbol = it->second;
		symbol->show();
		std::cout << ",";
	}
}

MapSym *  SymbolTable::getMap(){
	return this->symbols;
}
void SymbolTable::append(SymbolTable * symbTab) {
	symbols->insert(symbTab->getMap()->begin(), symbTab->getMap()->end());
}

SymbolTable *  SymbolTable::clone() {
	SymbolTable * newSymTable = new SymbolTable(this->getParent(), this->getCallingCont());

	for (MapSym::iterator it = symbols->begin(); it != symbols->end(); it++) {
		int valueInIdentTable = it->first;
		Symbol * symbol = it->second;

		newSymTable->add(valueInIdentTable,  symbol->clone());
	}
	return newSymTable;
}

bool SymbolTable::substAllTemp() {
	for (MapSym::iterator it = symbols->begin(); it != symbols->end(); it++) {
		//int valueInIdentTable = it->first;
		Symbol * symbol = it->second;

		if( symbol->getType() == NULL ) {
			continue;
		}

		if(TypePointer() == *symbol->getType() &&
				TypeTemp() == *((TypePointer*)symbol->getType())->getType()){

			TypeTemp * typeTemp = (TypeTemp *) ((TypePointer *)symbol->getType())->getType();
			std::cout << typeTemp->getTempId() << std::endl;
			typeTemp->show();

			Symbol * symType = this->getById( typeTemp->getTempId() );
			if(symType != NULL && SymbolType() != *symType){
				//std::cout << "dsaffffffffffffffffffffffffffffffffffffffffff" << std::endl;
				return false;
			}
			symbol->setType( new TypePointer(symType->getType()->clone()) );

		}
	}
	return true;
}

TypeTemp * SymbolTable::nextTempType(int idIdent){
	//this->waitingForType->push_back(idIdent);
	std::map<int,TypeTemp *>::iterator it= this->tempTypes->find(idIdent);

	TypeTemp * typeTemp;
	if(it!=tempTypes->end()) {
		typeTemp = it->second;
	} else {
		typeTemp = new TypeTemp( idIdent );
		(*this->tempTypes)[idIdent] = typeTemp;
	}
	return typeTemp;
}

bool SymbolTable::resTempTypes() {
	for (std::map<int, TypeTemp*>::iterator it = tempTypes->begin(); it != tempTypes->end(); it++) {
		int idIdent = it->first;
		TypeTemp * typeTemp = it->second;

		Symbol *symbol = this->getByIdInside(idIdent);
		if( symbol == NULL ) {
			return false;
		}
		if( SymbolType() != *symbol ){
			return false;
		}

		if( TypePointer() == *symbol->getType() ) {
			if(TypeRecord() == *((TypePointer*) symbol->getType())->getType()){
				typeTemp->setType( symbol->getType()->clone() );
				continue;
			}
			return false;
		} 

		if (TypeRecord() == *symbol->getType() ){
			typeTemp->setType( symbol->getType()->clone() );
			continue;
		}

		return false;
	}
	return true;
}
//		std::string print(IdentTable * identTable) {
//			printf("Has a parent table ? ");
//			if (parentTable != NULL) {
//				printf("YES");
//			} else {
//				printf("NO");
//			}
//			printf("\n");
//
//			printf("Symbols : \n");
//			for (MapSym::iterator it=symbols->begin() ; it != symbols->end(); it++) {
//				int valueInIdentTable = it->first;
//				std::string nameInIdentTable = identTable->getName(it->first);
//				Symbol * symbol = it->second;
//				printf("\t Ident value : %3d \t Ident name : %s \n", valueInIdentTable, nameInIdentTable.c_str());
//				printf("\t Symbol : %d", symbol); //symbol->print();
//			}
//		}
